﻿#define _CRT_SECURE_NO_WARNINGS 
#include"CentralCache.h"
#include"PageCache.h"
CentralCache CentralCache::_sInst;
//获取一个非空的span

//从中心缓存获取一定数量的对象给tread cache
size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t batchNum, size_t size)

{
	size_t index = SizeClass::Index(size);
	_spanLists[index]._mtx.lock();
	Span* span = GetOneSpan(_spanLists[index], size);
	assert(span);
	assert(span->_freeList);
	//从span中获取actualNum个对象
	start = span->_freeList;
	end = start;
	size_t actualNum = 1;
	size_t i = 0;
	while (i < batchNum - 1 && NextObj(end) != nullptr)
	{
		end = NextObj(end);
		++actualNum;
		++i;
	}
	span->_freeList = NextObj(end);
	NextObj(end) = nullptr;
	span->_useCount += actualNum;
	_spanLists[index]._mtx.unlock();
	return actualNum;
}

Span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	//查看当前spanlist是否还有未分配对象的span
	
	Span* it = list.Begin();
	while (it != list.End())
	{
		if (it->_freeList != nullptr)
		{
			
			return it;
		}
		else {
			it = it->_next;
		}
	}
	//将central cache的桶锁解开，这样如果其它线程释放内存对象回来，就不会阻塞了
	list._mtx.unlock();
	//找page cache要
	PageCache::GetInstance()->_pageMtx.lock();
	Span* span = PageCache::GetInstance()->NewSpan(SizeClass::NumMovePage(size));
	span->_isUse = true;
	span->_objSize = size;
	PageCache::GetInstance()->_pageMtx.unlock();
	//计算大块内存的起始地址和字节大小
	char* start = (char*)(span->_pageId << PAGE_SHIFT);
	size_t bytes = span->_n << PAGE_SHIFT;
	char* end = start + bytes;
	//用自由链表链接起来
	span->_freeList = start;
	start += size;
	void* tail = span->_freeList;
	while (start < end)
	{
		NextObj(tail) = start;
		tail = start;
		start += size;
	}
	NextObj(tail) = nullptr;
	list._mtx.lock();
	//将span挂到桶里面去
	list.PushFront(span);
	return span;
}
 
// 将⼀定数量的对象释放到span跨度
void CentralCache::ReleaseListToSpans(void* start, size_t size)
{
	size_t index = SizeClass::Index(size);
	_spanLists[index]._mtx.lock();
	while (start)
	{
		void* next = NextObj(start);
		Span* span = PageCache::GetInstance()->MapObjectToSpan(start);
		NextObj(start) = span->_freeList;
		span->_freeList = start;
		span->_useCount--;
		//说明span的切分出去的小块内存回来了
		//这个span就可以再回收给page cache，page cache可以尝试做前后页的合并
		if (span->_useCount == 0)
		{
			_spanLists[index].Erase(span);
			span->_freeList = nullptr;
			span->_next = nullptr;
			span->_prev = nullptr;
			//释放span给page cache ,使用page cache 的锁就行
			_spanLists[index]._mtx.unlock();

			PageCache::GetInstance()->_pageMtx.lock();
			PageCache::GetInstance()->ReleaseSpanToPageCache(span);
			PageCache::GetInstance()->_pageMtx.unlock();

			_spanLists[index]._mtx.lock();
		}
		//进行下一个内存对象的归还
		start = next;
	}

	_spanLists[index]._mtx.unlock();
}